﻿using System;
using System.Collections.Generic;
using FibMatrix.Schedule;
using UnityEngine;
using FibMatrix.Schedule;
using UnityEngine.Profiling;
using UObject = UnityEngine.Object;

namespace FibMatrix
{
    /// <summary>
    /// 某种资源模式的抽象类，包含一些公共实现，同一时刻只会有一种资源模式
    /// </summary>
    [FM_Mono.IgnoreGen]
    public abstract partial class AbstractResourceFacadeImpl : IResourceFacadeImplement
    {
        public AbstractResourceFacadeImpl()
        {
            
        }

        private List<IResourceLoadPipe> _syncPipelines = null;

        private List<IResourceLoadPipe> _asyncPipelines = null;

        private Dictionary<string, ResourcePipelineContext> _pipelineContexts = new Dictionary<string, ResourcePipelineContext>();
        
        // asset map, assetId -> obj
        protected Dictionary<string, AbstractLoadItem> _loadItemMap = new Dictionary<string, AbstractLoadItem>();

        // protected Dictionary<int, AbstractLoadItem> _refObjectMap = new Dictionary<int, AbstractLoadItem>();

        protected class RefObjectNode
        {
            public int instanceID = 0;  // 可能是资源的实例id，也可能是资源的实例的实例id
            public UObject refObj = null;
            public AbstractLoadItem hint = null;
            
            public RefObjectNode prev = null;
            public RefObjectNode next = null;
        }

        // 包含原始资源对象, 实例化对象（包括非资源对象实例化的对象）, 部分new GameObject
#if UNITY_EDITOR
        protected Dictionary<int, UObject> _nonHintObjects = new Dictionary<int, UObject>();
#endif
        // 实例id -> assetItem。外部释放资源或实例对象时，能找到其对应的assetItem
        protected Dictionary<int, RefObjectNode> _refObjects = new Dictionary<int, RefObjectNode>();
        protected RefObjectNode _refObjectsHead = null;
        protected RefObjectNode _refObjectsCursor = null;

        private ResourcePipelineContext _syncContext = null;

        /// <summary>
        /// 外部释放后，没有外部计数的资源会进入回收列表，此变量是表头
        /// </summary>
        private AbstractLoadItem _recyleHintHead = null;

        private double _lastUnloadUnusedAssetsTime = -1f;

        private float _unloadUnusedAssetInterval = -1f;

        private Queue<GameObject> _childQueue = new Queue<GameObject>();

#if UNITY_EDITOR
        // 编辑器中能记录游戏过程中每个hint的最大计数
        private Dictionary<string, int> _maxHintCount = new Dictionary<string, int>();
#endif
        
        /// <summary>
        /// 异步加载完成后的那个异步实例化队列中的项
        /// </summary>
        protected class InstanceQueueNode
        {
            public IResourcePipelineCallback request;
            public AbstractLoadItem hint;
            public UObject original;
            public InstanceQueueNode next = null;
            public InstanceQueueNode(IResourcePipelineCallback request, AbstractLoadItem hint, UObject original)
            {
                this.request = request;
                this.hint = hint;
                this.original = original;
            }
        }

        protected int _instanceUpdator = -1;
        protected InstanceQueueNode _instanceQueueHead = null;
        protected InstanceQueueNode _instanceQueueTail = null;
        private int _instanceOneFrameLimit = 16;
        
        public void Initialize(IResourceInitConfig initConfig)
        {
            this._lastUnloadUnusedAssetsTime = FibMatrix.RealTimer.elapsedSeconds;
            this._unloadUnusedAssetInterval = initConfig.unloadUnusedAssetsInterval;
            _instanceUpdator = GlobalScheduler.AddFrameLimitUpdator(1, OnInstanceQueueUpdate);
            GlobalScheduler.SetActive(_instanceUpdator,false);
            _instanceOneFrameLimit = initConfig.instanceQueueOneFrameLimit;
            this.DoInitialize(initConfig);
            this._syncPipelines = this.CreateSyncLoadPipes(initConfig);
            this._asyncPipelines = this.CreateAsyncLoadPipes(initConfig);
            this._syncContext = this.CreatePipelineContext();
        }


        protected virtual void DoInitialize(IResourceInitConfig initConfig)
        {

        }

        public abstract string GenerateLoadURL(string prefix, string localPath, string extension);

        /// <summary>
        /// 创建资源数据，具体的资源模式可以重载
        /// </summary>
        /// <param name="assetId">AssetConfig的AssetNode中的id</param>
        /// <param name="createNotExist"></param>
        /// <returns></returns>
        protected virtual AbstractLoadItem AcquireLoadItem(string assetId, bool createIfNotExist)
        {
            AbstractLoadItem item;
            if (!_loadItemMap.TryGetValue(assetId, out item) && createIfNotExist)
            {
                item = new DefaultLoadItem(assetId);
                _loadItemMap.Add(assetId, item);
            }
            return item;
        }
        
        protected IEnumerator<KeyValuePair<string, AbstractLoadItem>> GetLoadItemEnumerator()
        {
            return this._loadItemMap.GetEnumerator();
        }

        
        public bool HasObject(string assetId)
        {
            AbstractLoadItem hint = this.AcquireLoadItem(assetId, false);
            if (hint == null)
            {
                return false;
            }
            return hint.assetHandle != null;
        }

        public virtual bool CanUseLoadSync(string assetId)
        {
            return true;
        }

#if !DISABLE_SYNC_LOAD
        public UObject LoadObject(string assetId, bool instantiated = false)
        {
            if (Debug.isDebugBuild)
            {
                var h = AcquireLoadItem(assetId, false);
                var rc = h != null ? h.objRefCount : 0;
                AssetActionLogger.RecordAction(null, assetId, AssetBundleActionType.RequestAsset,
                    $"ARef={rc}");
            }
            Profiler.BeginSample("LoadObject");
            Profiler.BeginSample(assetId);
            UObject res = null;
            AbstractLoadItem hint = this.AcquireLoadItem(assetId, true);
            if (hint != null)
            {
                if (hint.instanceIDAquired)
                {
                    // 首次之后的请求资源走这里
                    res = Instantiate(hint.assetHandle.asset, hint, instantiated);
                }
                else
                {
                    var startData = AssetActionLogger.GetActionStartData();
                    _syncContext.FlowIn(hint, null, this._syncPipelines, null);
                    AssetActionLogger.RecordAction(startData, hint.itemID, AssetBundleActionType.LoadAsset, 
                        $"ARef={hint.objRefCount}");
                    
                    ResourceError error = _syncContext.error;
                    if (error == ResourceError.None)
                    {
                        var original = hint.assetHandle != null ? hint.assetHandle.asset : null;
                        SetInstanceIdToLoadItem(original, hint);
                        if (original)
                            res = Instantiate(original, hint, instantiated);
                    }
                }
            }
            Profiler.EndSample();
            Profiler.EndSample();
            return res;
        }
        
        public virtual T[] LoadAllAssetOnce<T>(string assetId) where T : UObject
        {
            return null;
        }
#endif
        

        /// <summary>
        /// 一个资源在异步加载完成之前，还有请求的话都会加到同一个context中，等加载完一起回调，不会多次加载
        /// 即使加载失败，仍然会回调给外部
        /// </summary>
        internal AsyncResourceLoadRequest LoadObjectAsync(string assetId, Type type, bool create, int priority)
        {
            if (Debug.isDebugBuild)
            {
                var h = AcquireLoadItem(assetId, false);
                var rc = h != null ? h.objRefCount : 0;
                AssetActionLogger.RecordAction(null, assetId, AssetBundleActionType.RequestAssetAsync,
                    $"ARef={rc}");
            }
            AbstractLoadItem hint = this.AcquireLoadItem(assetId, true);
            
            Profiler.BeginSample("DoLoadObjectAsync");
            Profiler.BeginSample(assetId);
            ResourcePipelineContext context = null;
            bool newContext = false;
            if (!this._pipelineContexts.TryGetValue(assetId, out context))
            {
                context = this.CreatePipelineContext();
                newContext = true;
            }
            else
            {
                // 返回同样的context
            }
            AsyncResourceLoadRequest request = new AsyncResourceLoadRequest(assetId, this, 
                context, hint, create, priority);
            context.Add(request);

            if (newContext)
            {
                context.startData = AssetActionLogger.GetActionStartData();
                bool flowFinish = !context.FlowIn(hint, type, this._asyncPipelines, this.OnPipelineComplete);
                if (flowFinish && context.error != ResourceError.None)
                {
                    Debug.LogError($"loadAsync failed:{assetId}, error:{context.error}");
                }

                // 即使有错误，没资源，让然执行pipeline进行回调
                //异步加载冗余一份引用计数，表示资源管理内部对其的引用，防止加载时被卸载掉, OnPipelineComplete时会删掉
                if (hint != null)
                    ++hint.objRefCount;
                this._pipelineContexts.Add(assetId, context);
                // 立即完成flow，没法基于task异步回调，这里补上
                if (flowFinish)
                    GlobalScheduler.CallOnceFrame(1, o =>
                    {
                        OnPipelineComplete(context, context.error,
                            new List<IResourcePipelineCallback>() { request });
                    });
            }

            Profiler.EndSample();
            Profiler.EndSample();
            
            return request;
        }
        
        public AbstractLoadItem AcquireLoadItemByInstanceId(int instanceId)
        {
            RefObjectNode item;
            this._refObjects.TryGetValue(instanceId, out item);
            return item != null ? item.hint : null;
        }

        public UObject Instantiate(UObject original, AbstractLoadItem hint, bool instantiated = true)
        {
            if (original == null)
            {
                string msg = null;
                if (hint != null)
                {
                    msg = $"url:{hint.itemID}, refCount:{hint.objRefCount}";
                }
                throw new ArgumentNullException(msg);
            }
            UObject newObj = instantiated ? UObject.Instantiate(original) : original;
            if (hint != null)
            {
                // 对资源或实例加计数，在destroy时减掉  
                ++hint.objRefCount;
            }
            if (instantiated)
            {
                AddRefObject(newObj, hint);
            }
            return newObj;
        }

        public GameObject NewGameObject(string name = "")
        {
            var go = new GameObject(name);
            
            AddRefObject(go, null);

            return go;
        }

        /// <summary>
        /// 释放资源，外部逻辑会调用到此接口，用于减计数
        /// 计数达到要求时会放入回收队列，等待进一步处理
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="external"></param>
        /// <param name="delayTime"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public void Destroy(UObject obj, bool external, float delayTime = 0)
        {
            var origObj = obj;
            if (obj == null)
            {
#if DEBUG
                throw new ArgumentNullException();
#else
    return;
#endif
            }

#if UNITY_EDITOR
            // Debug.Log($"ResourceFacadeImpl Destroy: {obj.name}.{obj.GetInstanceID()}");
#endif
            if (obj is Component)
            {
                // UObject.Destroy(obj);
                // return;
                obj = (obj as Component).gameObject;
            }

            int instanceID = obj.GetInstanceID();
            RefObjectNode node;
            AbstractLoadItem hint;
            _refObjects.TryGetValue(instanceID, out node);
            if (node != null)
            {
                hint = node.hint;
                if (hint != null)
                {
                    if (hint.objRefCount > 1)
                    {
                        --hint.objRefCount;
                    }

                    if (external)
                        AssetActionLogger.RecordAction(null, hint.itemID, 
                            AssetBundleActionType.ReleaseAsset, $"ARef={hint.objRefCount}");
                    if (hint.objRefCount == 1)
                    {
                        RecycleHint(hint, delayTime);
                    }

                    if (instanceID == hint.instanceID)
                    {
                        return;
                    }
                }
                RemoveRefObject(node);
            }
            else
            {
#if UNITY_EDITOR
                if (_nonHintObjects.ContainsKey(instanceID))
                {
                    _nonHintObjects.Remove(instanceID);
                }
                else
                {
                    // 重复的销毁，或者销毁错误
                }
#endif
            }

// #if UNITY_EDITOR
//             if (obj.GetType() == typeof(GameObject))//是GameObject的话，一定是Prefab的实例
//             {
//                 CheckHasManagedObjectInChildren(obj as GameObject);
//             }
// #endif
            
            if (delayTime > 0)
            {
                UObject.Destroy(obj, delayTime);
            }
            else
            {
                UObject.Destroy(obj);
            }
        }

        /// <summary>
        /// 遍历回收队列，满足要求的资源和bundle会被卸载
        /// </summary>
        /// <param name="forceDelete"></param>
        /// <param name="forceUnloadUnusedAsset"></param>
        public void CheckReourceRelease(bool forceDelete = false, bool forceUnloadUnusedAsset = false)
        {
            AbstractLoadItem hint = _recyleHintHead;
            AbstractLoadItem prev = null;
            bool needUnloadUnusedAsset = false;
            while (hint != null)
            {
                bool del = false;
                if (hint.objRefCount > 1)   // 如果外部又请求了这个hint要从回收队列里去掉
                {
                    del = true;
                }
                else if (FibMatrix.RealTimer.elapsedSeconds >= hint.destoryTime || forceDelete)
                {
                    del = true;
                    needUnloadUnusedAsset = ReleaseLoadItem(hint);
                }

                var nextHint = hint.next;
                if (del)
                {
                    if (prev == null)
                    {
                        _recyleHintHead = nextHint;
                    }
                    else
                    {
                        prev.next = nextHint;
                    }
                    hint.inRecycle = false;
                    hint.next = null;
                }
                else
                {
                    prev = hint;
                }

                // hint = hint.next;   // 每次只能释放一个
                hint = nextHint;    // 每次尽量释放
            }

            if (forceUnloadUnusedAsset || 
                (needUnloadUnusedAsset && 
                 (RealTimer.elapsedSeconds - _lastUnloadUnusedAssetsTime >= _unloadUnusedAssetInterval)))
            {
                _lastUnloadUnusedAssetsTime = RealTimer.elapsedSeconds;
                Resources.UnloadUnusedAssets();
            }

#if UNITY_EDITOR
            var iter = this.GetLoadItemEnumerator();
            while (iter.MoveNext())
            {
                hint = iter.Current.Value;
                int maxHintCount = 0;
                if (!_maxHintCount.TryGetValue(hint.itemID, out maxHintCount))
                {
                    _maxHintCount.Add(hint.itemID, hint.objRefCount);
                }
                else
                {
                    if (hint.objRefCount > maxHintCount)
                    {
                        Debug.LogWarning(string.Format("引用大于进游戏后的最大引用次数<color=red>({0}次)</color>：{1}", maxHintCount, hint.itemID));
                        _maxHintCount[hint.itemID] = hint.objRefCount;
                    }
                }
            }
#endif
        }

        /// <summary>
        /// 回收没有外部引用的资源
        /// </summary>
        public void ClearUnhandleResources()
        {
            var iter = this.GetLoadItemEnumerator();
            while (iter.MoveNext())
            {
                if (iter.Current.Value.objRefCount == 1)
                {
                    RecycleHint(iter.Current.Value, 0f);
                }
                else
                {
#if UNITY_EDITOR
                    Debug.LogWarning($"AbstractResourceFacadeImpl::ClearUnhandleResources, Hint Ref Count > 1 {iter.Current.Value.itemID}, {iter.Current.Value.objRefCount}");
#endif
                }
            }
        }

        #region 资源引用

        /// <summary>
        /// 记录资源或实例 跟 hint的关系
        /// </summary>
        /// <param name="refObj"></param>
        /// <param name="hint"></param>
        private void AddRefObject(UObject refObj, AbstractLoadItem hint)
        {
            if (hint != null)
            {
                var node = new RefObjectNode();
                node.instanceID = refObj.GetInstanceID();
                node.refObj = refObj;
                node.hint = hint;

                node.prev = null;
                node.next = null;

                if (_refObjectsHead == null)
                {
                    _refObjectsHead = node;
                }
                else
                {
                    node.next = _refObjectsHead;
                    _refObjectsHead.prev = node;

                    _refObjectsHead = node;
                }

                _refObjects.Add(node.instanceID, node);
#if UNITY_EDITOR
                // Debug.Log($"AddRefObject. {node.instanceID}.{(node.hint != null ? node.hint.url : node.refObj.name)}[{(node.hint != null && node.instanceID == node.hint.instanceID ? "Original" : "Instance")}]");
#endif
            }
            else
            {
#if UNITY_EDITOR
                _nonHintObjects.Add(refObj.GetInstanceID(), refObj);
#endif
            }
        }

        protected void RemoveRefObject(int instanceID)
        {
            if (_refObjects.TryGetValue(instanceID, out RefObjectNode node))
            {
                RemoveRefObject(node);
            }
        }

        private void RemoveRefObject(RefObjectNode node)
        {
            // head
            if (_refObjectsHead == node)
                _refObjectsHead = node.next;
            // not tail
            if (node.next != null)
                node.next.prev = node.prev;
            // not head
            if (node.prev != null)
                node.prev.next = node.next;

            _refObjects.Remove(node.instanceID);

            if (node == _refObjectsCursor)
            {
                _refObjectsCursor = node.next;
            }
        }

        private void ClearRefObjects()
        {
            _refObjects.Clear();
            
            // clear dlink
            var currentNode = _refObjectsHead;
            while (currentNode != null)
            {
                RemoveRefObject(currentNode);
                currentNode = currentNode.next;
            }

            _refObjectsHead = null;
            _refObjectsCursor = null;
#if UNITY_EDITOR
            _nonHintObjects.Clear();
#endif
        }
        
        /// <summary>
        /// 分帧释放那些 非正规销毁的实例对应的资源
        /// 如果用了ResourceFacade加载了资源，但没用ResourceFacade接口销毁，而是用unity接口销毁
        /// 那么本函数可尝试减掉这种资源的一个计数，并尝试回收
        /// </summary>
        public void RefObjectPeriodicCheck()
        {
            var enterTime = FibMatrix.RealTimer.elapsedMilliseconds;

            if (_refObjectsCursor == null)
            {
                _refObjectsCursor = _refObjectsHead;
            }
            var currentNode = _refObjectsCursor;
            while (currentNode != null)
            {
                if (currentNode.refObj == null)
                {
                    if (currentNode.hint != null)
                    {
                        if (currentNode.hint.objRefCount > 1)
                        {
                            --currentNode.hint.objRefCount;
                        }

                        RemoveRefObject(currentNode);

                        if (currentNode.hint.objRefCount == 1)
                        {
                            RecycleHint(currentNode.hint, 0f);
                        }
                    }
                    else
                    {
                        RemoveRefObject(currentNode);
                    }
                }

                currentNode = currentNode.next;

                if (FibMatrix.RealTimer.elapsedMilliseconds - enterTime > 1000)
                {
                    break;
                }
            }
            _refObjectsCursor = currentNode;
        }

        /// <summary>
        /// 一次行全部 释放那些 非正规销毁的实例对应的资源
        /// </summary>
        public void RefObjectCheckInGC()
        {
            var currentNode = _refObjectsHead;
            while (currentNode != null)
            {
                if (currentNode.refObj == null)
                {
                    if (currentNode.hint != null)
                    {
                        if (currentNode.hint.objRefCount > 1)
                        {
                            --currentNode.hint.objRefCount;
                        }

                        RemoveRefObject(currentNode);

                        if (currentNode.hint.objRefCount == 1)
                        {
                            RecycleHint(currentNode.hint, 0f);
                        }
                    }
                    else
                    {
                        RemoveRefObject(currentNode);
                    }
                }

                currentNode = currentNode.next;
            }
        }

        public void ReportRefObjectCheck()
        {
            var currentNode = _refObjectsHead;
            while (currentNode != null)
            {
                if (currentNode.hint != null)
                {
                    if (currentNode.instanceID != currentNode.hint.instanceID)
                    {
                        Debug.LogWarning($"ReportRefObjectCheck.{currentNode.refObj.name}.{currentNode.instanceID}[Instance] resource leak in GC");
                    }
                    else if (currentNode.hint.objRefCount > 1)
                    {
                        Debug.LogWarning($"ReportRefObjectCheck.{currentNode.refObj.name}.{currentNode.instanceID}[Original] ref.{currentNode.hint.objRefCount} resource leak in GC");
                    }
                }
                else
                {
                    Debug.LogWarning($"ReportRefObjectCheck.{currentNode.refObj.name}.{currentNode.instanceID} resource leak in GC");
                }
                currentNode = currentNode.next;
            }
        }
        
#if UNITY_EDITOR
        public void ReportNonhintRefObjectCheck()
        {
            var it = _nonHintObjects.GetEnumerator();
            while (it.MoveNext())
            {
                if (it.Current.Value != null)
                {
                    Debug.LogWarning($"ReportRefObjectCheck.Nonhint {it.Current.Value.name}.{it.Current.Key} resource leak in GC");
                }
            }
            _nonHintObjects.Clear();
        }
#endif
        #endregion

        public void Dispose()
        {
            ClearRefObjects();
            ClearInstanceQueue();
            this.DestroyPipelineContext(this._syncContext);
            this._syncContext = null;
            var iter = this._pipelineContexts.GetEnumerator();
            while (iter.MoveNext())
            {
                this.DestroyPipelineContext(iter.Current.Value);
            }
            this._pipelineContexts.Clear();
            this.UnloadAll();
        }

        protected abstract void UnloadAll();

        protected abstract List<IResourceLoadPipe> CreateSyncLoadPipes(IResourceInitConfig initConfig);

        protected abstract List<IResourceLoadPipe> CreateAsyncLoadPipes(IResourceInitConfig initConfig);

        protected abstract ResourcePipelineContext CreatePipelineContext();

        protected abstract void DestroyPipelineContext(ResourcePipelineContext context);


        protected abstract bool ReleaseLoadItem(AbstractLoadItem item);

        void SetInstanceIdToLoadItem(UObject original, AbstractLoadItem hint)
        {
            if (original)
            {
                if (!hint.instanceIDAquired)
                {
                    hint.instanceID = original.GetInstanceID();
                    hint.instanceIDAquired = true;
                    AddRefObject(original, hint);
                }
            }
        }

        protected void RemoveInstanceIdFromLoadItem(AbstractLoadItem item)
        {
            if (item.instanceIDAquired)
            {
                // this._refObjectMap.Remove(item.instanceID);
                RemoveRefObject(item.instanceID);
                item.instanceIDAquired = false;
            }
        }

        private void OnPipelineComplete(ResourcePipelineContext context, ResourceError error, List<IResourcePipelineCallback> requests)
        {
            UObject original = null;
            var hint = context.item;
            if (hint != null)
            {
                --hint.objRefCount; // 释放资源管理对异步加载过程的计数
                this._pipelineContexts.Remove(hint.itemID);
                AssetActionLogger.RecordAction(context.startData, hint.itemID, AssetBundleActionType.LoadAssetAsync,
                    $"ARef={hint.objRefCount}");
                this.DestroyPipelineContext(context);
                if (error == ResourceError.None)
                {
                    original = hint.assetHandle != null ? hint.assetHandle.asset : null;
                    SetInstanceIdToLoadItem(original, hint);
                }
            }

            try
            {
                if (original == null)
                    Debug.LogError($"load resouces failed:{hint?.itemID}, {error}");
                
                for (int i = 0; i < requests.Count; ++i)
                {
                    IResourcePipelineCallback request = requests[i];
                    if (request == null || request.destroyed)
                    {
                        continue;
                    }

                    bool needInstantiate = request.instantiated && original != null;
                    if (needInstantiate)
                    {
                        AddToInstanceQueue(request, hint, original);
                    }
                    else
                    {
                        request.OnAssetLoadComplete(original);
                    }
                }
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }
        }

        /// <summary>
        /// 按request优先级添加到实例化队列，前面的优先级高
        /// </summary>
        /// <param name="request"></param>
        /// <param name="hint"></param>
        /// <param name="original"></param>
        private void AddToInstanceQueue(IResourcePipelineCallback request,AbstractLoadItem hint,UObject original)
        {
            // 异步实例化队列加计数，实例化后会减
            ++hint.objRefCount;
            InstanceQueueNode node = new InstanceQueueNode(request,hint,original);
            if (_instanceQueueHead == null)
            {
                _instanceQueueHead = node;
                _instanceQueueTail = node;
            }
            else if (request.priority == -1)
            {
                node.next = _instanceQueueHead;
                _instanceQueueHead = node;
            }
            else if (request.priority == 0)
            {
                _instanceQueueTail.next = node;
                _instanceQueueTail = node;
            }
            else
            {
                InstanceQueueNode tmpNode = _instanceQueueHead;
                while (tmpNode != null)
                {
                    if (node.request.priority > tmpNode.request.priority)
                    {
                        if (tmpNode == _instanceQueueHead)
                        {
                            _instanceQueueHead = node;
                        }
                        node.next = tmpNode;
                        break;
                    }

                    if (tmpNode == _instanceQueueTail)
                    {
                        tmpNode.next = node;
                        _instanceQueueTail = node;
                        break;
                    }
                    
                    tmpNode = tmpNode.next;
                }
            }
 
            if (_instanceQueueHead != null)
            {
                GlobalScheduler.SetActive(_instanceUpdator,true);
            }
        }
        
        private void OnInstanceQueueUpdate(object obj)
        {
            long startTime = FibMatrix.RealTimer.elapsedMilliseconds;
            var node = _instanceQueueHead;
            
            while (node != null)
            {
                var next = node.next;
                
                try
                {
                    if (!node.request.destroyed)
                    {
                        node.request.OnAssetLoadComplete(node.original);//这里返回的都是Asset，在OnAssetLoadComplete里决定是否创建实例
                    }
                }
                catch (Exception e)
                {
                    Debug.LogException(e);
                }
                finally
                {
                    // 对应添加到实例化队列那次加计数
                    --node.hint.objRefCount;
                }
                
                var now = FibMatrix.RealTimer.elapsedMilliseconds;

                // Debug.Log($"ResourceFacade::Instance One...spend:{now - startTime}  url:{node.hint.url}");

                if (now - startTime > _instanceOneFrameLimit)
                {
                    break;
                }
                node = next;
            }

            _instanceQueueHead = node?.next;

            if (_instanceQueueHead == null)
            {
                _instanceQueueTail = null;
                GlobalScheduler.SetActive(_instanceUpdator,false);
            }
        }

        private void ClearInstanceQueue()
        {
            _instanceQueueHead = null;
            _instanceQueueTail = null;

            GlobalScheduler.RemoveLimitUpdator(ref _instanceUpdator);
        }

        /// <summary>
        /// 把资源放到回收队列
        /// </summary>
        /// <param name="hint"></param>
        /// <param name="delayTime"></param>
        private void RecycleHint(AbstractLoadItem hint, float delayTime)
        {
            hint.destoryTime = Math.Max(hint.destoryTime, FibMatrix.RealTimer.elapsedSeconds + 2 + delayTime);
            if (!hint.inRecycle)
            {
                hint.next = _recyleHintHead;
                _recyleHintHead = hint;
                hint.inRecycle = true;
            }
            //              CustomLog("Prepare to Destroy " + hint.id);
        }

        //TODO:重构
        /// <summary>
        /// 检测是否该物体的所有子物体里，有没有存在从ResourceFacade里Load出来的对象。
        /// </summary>
        private void CheckHasManagedObjectInChildren(GameObject obj)
        {
            _childQueue.Clear();
            _childQueue.Enqueue(obj);
            int count = obj.transform.childCount;
            for (int i = 0; i < count; ++i)
            {
                Transform child = obj.transform.GetChild(i);
                _childQueue.Enqueue(child.gameObject);
            }
            while (_childQueue.Count > 0)
            {
                GameObject go = _childQueue.Dequeue();
                int id = go.GetInstanceID();
                if (_refObjects.ContainsKey(id))
                {
                    Debug.LogError(string.Format("There is a managed child:{0} in {1}", go.name, obj.name));
                }
                int childCount = go.transform.childCount;
                for (int i = 0; i < childCount; ++i)
                {
                    Transform child = go.transform.GetChild(i);
                    _childQueue.Enqueue(child.gameObject);
                }
            }
        }
    }
}
